home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 May: Tool Chest / Developer CD Series May 1996 (Tool Chest) (Apple Computer) (1996).iso / Sample Code / Snippets / Toolbox / Reinstallable / Reinstallable.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-10-07  |  9.9 KB  |  363 lines  |  [TEXT/KAHL]

  1. /*
  2.  *  Sample reinstallable init
  3.  *  v 1.0
  4.  *
  5.  *  August 1993  Greg Robbins
  6.  *
  7.  *  This sample INIT patches a trap globally yet is reinstallable:
  8.  *  it can be recompiled and run without rebooting.
  9.  *
  10.  *  Usually, when an INIT patches a trap, changing the INIT requires
  11.  *  reinstalling the INIT in the Extensions folder and rebooting.
  12.  *  This INIT demonstrates a technique which allows new INIT code to
  13.  *  replace most of the old code without the developer having to reboot.
  14.  *
  15.  *  This INIT just patches Standard File (_Pack3) and beeps when
  16.  *  a standard file dialog is raised.
  17.  *
  18.  *  Demonstration:
  19.  *
  20.  *  • put the Reinstallable Init in the Extensions folder and reboot
  21.  *  • bring up a standard file dialog; notice the beep
  22.  *  • drag the Reinstallable twoBeep INIT file onto the
  23.  *    LaunchInits program
  24.  *  • bring up a standard file dialog again and notice both beeps 
  25.  *
  26.  *  The trick is to keep a global handle in the system heap containing 
  27.  *  the addresses of the routines we want executed for each
  28.  *  patched item.  The handle to the globals is available via Gestalt.
  29.  *
  30.  *  A permanently installed dispatcher routine (part of the init code)
  31.  *  is used to get the address of our patch routine and jump to it.
  32.  *
  33.  *  The second time the init code runs, it will use the existing global
  34.  *  handle, and just update the addresses of our patch routines so the
  35.  *  dispatcher code jumps to the correct places.
  36.  *
  37.  *  A couple of critical items:
  38.  *    - The init loaded at boot time contains the patch dispatchers, so
  39.  *      it must never be disposed of.  We will simply strand it in the
  40.  *      heap when the first reinstallation occurs.
  41.  *    - If we change anything about which routines are patched, this invalidates
  42.  *      the global handle struct format, so we must reboot
  43.  *    - Similarly, if the dispatching code is changed, we must reboot
  44.  *      since only the dispatching code from the first time the init is
  45.  *      installed can be used.
  46.  *    - The dispatch and patch routines destroy registers, so they
  47.  *      may not be suitable for OS patches
  48.  *
  49.  *  Globals:
  50.  *
  51.  *  Since this code only uses a single true global (a handle to our
  52.  *  globals in the system heap) I used a simpler method than Think C's
  53.  *  A4 globals.  The global handle is stored smack in the middle of the
  54.  *  GetGlobalsHandleFromStorage routine. Calling GGHFS with a handle
  55.  *  stores the handle there; calling GGHFS with no parameter returns
  56.  *  the stored handle.  (The handle can also be retrieved from
  57.  *  Gestalt, but that is too slow a method for use in a trap patch.)
  58.  *  
  59.  */
  60.  
  61. #define SystemSevenOrLater 1    // leave out unnecessary glue code
  62.  
  63. #include <Memory.h>
  64. #include <GestaltEqu.h>
  65. #include <StandardFile.h>
  66. #include <Resources.h>
  67. #include <Events.h>
  68. #include <SysEqu.h>
  69. #include <Traps.h>
  70.  
  71. #define kOptionKeyCode 0x3A
  72.  
  73. #define kReinstInitSelector 'Réiñ'    // something suitably obscure
  74.  
  75. // global types
  76.  
  77. typedef struct Globals {
  78.     Size    globalsSize;    // size of this struct
  79.     Handle    initHandle;        // handle for this INIT if reinstalled
  80.     
  81.     // traps and hooks
  82.     ProcPtr    pack3TrueAddr;  // old trap address
  83.     ProcPtr pack3PatchAddr; // my patch routine address
  84.     
  85.     ProcPtr reinstInitGestaltFunctionAddr;
  86.                             // address of my Gestalt function
  87.     
  88.     // other globals can go here
  89.     
  90. } Globals, *GlobalsPtr, **GlobalsHandle;
  91.  
  92. // prototypes
  93.  
  94. Boolean KeyIsDown(short);
  95. GlobalsHandle GetGlobalsHandleFromStorage(GlobalsHandle);
  96. void SetGlobalsHandleToStorage(GlobalsHandle *);
  97. void ReinstInitGestaltDispatch(void);
  98. void Pack3PatchDispatch(void);
  99. pascal OSErr ReinstInitGestaltFunction (OSType, long *);
  100. void Pack3Patch(void);
  101.  
  102.  
  103.  
  104. // installation routine
  105.  
  106. main()
  107. {
  108.  
  109.     Handle            initHandle, previousInitHandle;
  110.     GlobalsHandle    gHandle;
  111.     Boolean         installedFlag, firstInstallFlag;
  112.     OSErr            retCode;
  113.     long            gestaltResponse;
  114.     
  115.     // Think C points A0 at the start of the block;
  116.     // use it to get the handle to the INIT resource
  117.     asm {
  118.         RecoverHandle
  119.         MOVE.L    A0, initHandle
  120.     }
  121.  
  122.     // never hurts to lock the block of code we are in
  123.     HLock(initHandle);
  124.     
  125.     // since this INIT uses the "lazy" installation method
  126.     // (it's just a detached resource) it's critical that
  127.     // the INIT resource had its system heap bit 
  128.     // set.  I won't check for that here.
  129.     
  130.     // flag that this has not yet successfully installed
  131.     installedFlag = false;
  132.  
  133.     // don't install if the Option key is depressed
  134.     if (KeyIsDown(kOptionKeyCode))
  135.         goto Bail;
  136.     
  137.     
  138.     // check that System 7 is available
  139.     retCode = Gestalt(gestaltSystemVersion, &gestaltResponse);
  140.     if (retCode != noErr || gestaltResponse < 0x0700)
  141.         goto Bail;
  142.  
  143.     // find if my global struct already exists
  144.     retCode = Gestalt(kReinstInitSelector, &gestaltResponse);
  145.     
  146.     if (retCode != noErr || gestaltResponse == 0) {
  147.         
  148.         // this is the first installation
  149.         
  150.         // install Gestalt selector to return handle of our globals    
  151.         retCode = NewGestalt(kReinstInitSelector, 
  152.             ReinstInitGestaltDispatch);
  153.         if (retCode != noErr) goto Bail;
  154.     
  155.         // no existing globals, so allocate them
  156.  
  157.         gHandle = (GlobalsHandle) NewHandleSys(sizeof(Globals));
  158.         if (gHandle == nil) goto Bail;
  159.  
  160.         // record the size of the globals
  161.         (**gHandle).globalsSize = sizeof(Globals);
  162.         
  163.         // since this is the first install, this INIT's handle
  164.         // must never be disposed to protect our dispatchers
  165.  
  166.         (**gHandle).initHandle = nil;
  167.  
  168.         // we are not reinstalling the INIT
  169.         firstInstallFlag = true;
  170.     }
  171.     else {
  172.         
  173.         // this is a reinstallation
  174.         
  175.         // get the handle for the existing globals
  176.         gHandle = (GlobalsHandle) gestaltResponse;
  177.  
  178.         // ensure that the globals block is the current size
  179.     
  180.         if ((**gHandle).globalsSize != sizeof(Globals)) {
  181.         
  182.             SetHandleSize((Handle) gHandle, sizeof(Globals));
  183.             retCode = MemError();
  184.             if (retCode != noErr) goto Bail;
  185.         }
  186.         
  187.         // update or reset any globals here
  188.         
  189.         // get rid of the last incarnation of the INIT code
  190.         // (unless it was the first)
  191.         
  192.         previousInitHandle = (**gHandle).initHandle;
  193.         if (previousInitHandle != nil)
  194.             DisposeHandle(previousInitHandle);
  195.         
  196.         // save the handle to this version of the INIT
  197.         
  198.         (**gHandle).initHandle = initHandle;
  199.         
  200.         // we are reinstalling the INIT
  201.         firstInstallFlag = false;
  202.     }
  203.  
  204.     // okay, we're committed to installation    
  205.     installedFlag = true;
  206.     
  207.     // make the INIT float free
  208.     DetachResource(initHandle);
  209.     HLock(initHandle);
  210.  
  211.     // save the globals handle into the GetGlobalsHandleFromStorage code
  212.     (void) GetGlobalsHandleFromStorage(gHandle);
  213.     
  214.     // patch _Pack3 trap (Standard File Package)
  215.     
  216.     if (firstInstallFlag) {
  217.     
  218.         // save the old trap address, then point the trap at my dispatch
  219.         // function
  220.         
  221.         (**gHandle).pack3TrueAddr = (ProcPtr) GetToolTrapAddress(_Pack3);
  222.         SetToolTrapAddress((long) Pack3PatchDispatch, _Pack3);
  223.     }
  224.     
  225.     // store the address of my patch in the globals
  226.     // so the dispatcher knows where to go
  227.     
  228.     (**gHandle).pack3PatchAddr = Pack3Patch;
  229.     
  230.     // store the address of my gestalt function in the globals
  231.     (**gHandle).reinstInitGestaltFunctionAddr = ReinstInitGestaltFunction;
  232.  
  233.     
  234.  
  235. Bail:
  236.     
  237.     // could do a ShowINIT here based on the installedFlag
  238.     if (!installedFlag) DebugStr("\p Reinstallable INIT installation failed");
  239. }
  240.  
  241.  
  242. // Dispatch routines
  243. //
  244. // These routines are the targets of any trap and hook addresses we
  245. // installed at INIT time (the Gestalt function as well)
  246. //
  247. // This code cannot change or move until reboot.  We will strand the
  248. // first version of the INIT code that is installed to ensure
  249. // that these routines remain present.  These routines are not
  250. // used in the reinstalled code (i.e. the routines from the first 
  251. // install will continue to be called)
  252. //
  253. // If a lot of trap patches are in place, a more general dispatcher
  254. // might be appropriate
  255.  
  256. void ReinstInitGestaltDispatch()
  257. {
  258.     // dispatch to my Gestalt routine
  259.     
  260.     // destroys D0, A0
  261.     asm {
  262.         MOVE.L    #0, -(SP)                    ; parameter to GetGlobalsHandle...
  263.         JSR        GetGlobalsHandleFromStorage
  264.         ADDQ.L    #4, SP                        ; dispose of parameter
  265.         MOVE.L    D0, A0                        ; deref the globals handle
  266.         MOVE.L    (A0), A0                    ;   to get the routine address
  267.         MOVE.L    Globals.reinstInitGestaltFunctionAddr(A0), A0
  268.         JMP        (A0)                        ; jump to the routine
  269.     }
  270. }
  271.  
  272. void Pack3PatchDispatch()
  273. {
  274.     // dispatch to my Pack3 trap patch
  275.     // destroys A0, D0
  276.     asm {
  277.         MOVE.L    #0, -(SP)
  278.         JSR        GetGlobalsHandleFromStorage
  279.         ADDQ.L    #4, SP
  280.         MOVE.L    D0, A0
  281.         MOVE.L    (A0), A0
  282.         MOVE.L    Globals.pack3PatchAddr(A0), A0;
  283.         JMP        (A0)
  284.     }
  285. }
  286.  
  287. //
  288. // Patch routines and other meat code
  289. //
  290.  
  291. void Pack3Patch()
  292. {
  293.     // patch to Standard File
  294.     // just beeps before the dialog is raised
  295.     
  296.     SysBeep(10);
  297.     
  298.     asm {
  299.         // get the address of the real standard file trap
  300.         // and jump to it
  301.         
  302.         MOVE.L    #0, -(SP)                    ; parameter to GetGlobalsHandle...
  303.         JSR        GetGlobalsHandleFromStorage
  304.         ADDQ.L    #4, SP                        ; dispose of parameter
  305.         MOVE.L    D0, A0                        ; deref the globals handle
  306.         MOVE.L    (A0), A0                    ; to get the real trap address
  307.         MOVE.L    Globals.pack3TrueAddr(A0), A0
  308.         JMP        (A0)                        ; jump to it
  309.     }
  310. }
  311.  
  312. // this Gestalt function returns the address of the globals block
  313. pascal OSErr ReinstInitGestaltFunction (OSType selector, long * response)
  314. {
  315.     *response = (long) GetGlobalsHandleFromStorage(nil);
  316.  
  317.     return noErr;
  318. }
  319.  
  320.  
  321. // Keith Rollin's utility for using GetKeys from C
  322. // returns true if the Key with the given KeyCode is down
  323.  
  324. Boolean KeyIsDown(short keyCode)
  325. {
  326.     union {
  327.         KeyMap asMap;
  328.         unsigned char asBytes[16];
  329.     } myMap;
  330.  
  331.     GetKeys(myMap.asMap);
  332.     return ((myMap.asBytes[keyCode >> 3] >> 
  333.         (keyCode & 0x07)) & 1) != 0;
  334. }
  335.  
  336. // GetGlobalsHandleFromStorage lets us magically store a handle
  337. // in the code's block of memory
  338. //
  339. // passing in a value for gHandle saves the value; calling this
  340. // routine with nil later retrieves the value
  341.  
  342. GlobalsHandle GetGlobalsHandleFromStorage(GlobalsHandle gHandle)
  343. {
  344.     GlobalsHandle *gHandlePtr;
  345.     
  346.     // get the address of our handle storage space
  347.     
  348.     asm {
  349.         BSR        @1                    ; push the address of the saved space
  350.         
  351.         DC.L    0                    ; the saved handle will go here
  352.         
  353.     @1
  354.         MOVE.L    (SP)+, gHandlePtr    ; pop the address off of the stack
  355.     }
  356.     
  357.     // use the pointer to store the handle if we were passed a handle
  358.     if (gHandle) *gHandlePtr = gHandle;
  359.     
  360.     // return the handle that is there now
  361.     return *gHandlePtr;
  362. }
  363.